import os

'''
pygame imports
'''
import pygame
from pygame.locals import *

'''
Far too many Albow imports.
'''
from albow.layout import Row, Column, Grid
from albow.controls import Button, ImageButton, Label, Image
from albow.dialogs import * # alert
from albow.resource import get_font
from albow.screen import Screen
from albow.shell import Shell

'''
Gloom imports
'''
from utils import *

import net

# The following class has been taken from www.pygame.org
# http://www.pygame.org/wiki/SimpleFontManager
# Thank you, pygame guys.
class cFontManager(object):
    '''
    Written by Scott O. Nelson  
    '''
    def __init__(self, font_dict):
        self._fontDict = {}
        for font_id, pair in font_dict.items():
            assert 2 <= len(pair) <=3 , "Font record must be composed of a font name and a size - ('arial', 24). An optional third parameter may be specified for a custom search path."
            if len(pair) == 2:
                name, size = pair
                if name:
                    fontFullFileName = pygame.font.match_font(name)
                    assert fontFullFileName, 'Font: %s Size: %d is not available.' % pair
                else:
                    fontFullFileName = None # use default font
                
            else:
                name, size, path = pair
                fontFullFileName = os.path.join(path, name)
            self._fontDict[font_id] = pygame.font.Font(fontFullFileName, size)

    def Draw(self, font_id, text, surface, rectOrPosToDrawTo, color = pygame.color.Color("white"), alignHoriz='center', alignVert='center', antialias=True):
        '''
        Draw text with the given parameters on the given surface.
        '''
        assert self._fontDict.has_key(font_id), 'Font %s is not available in cFontManager.' % font_id
        fontSurface = self._fontDict[font_id].render(text, antialias, color)

        if isinstance(rectOrPosToDrawTo, tuple):
            surface.blit(fontSurface, rectOrPosToDrawTo)
        elif isinstance(rectOrPosToDrawTo, pygame.Rect):
            fontRect = fontSurface.get_rect()

            if alignHoriz == 'center':
                fontRect.centerx = rectOrPosToDrawTo.centerx
            elif alignHoriz == 'right':
                fontRect.right = rectOrPosToDrawTo.right
            else:
                fontRect.x = rectOrPosToDrawTo.x  # left

            if alignVert == 'center':
                fontRect.centery = rectOrPosToDrawTo.centery
            elif alignVert == 'bottom':
                fontRect.bottom = rectOrPosToDrawTo.bottom
            else:
                fontRect.y = rectOrPosToDrawTo.y  # top

            surface.blit(fontSurface, fontRect)


def play_button():
    #print gs.next_command
    required_keys = ["played_card", "target_card", "target_player"]
    for key in required_keys:
        if not gs.next_command.has_key(key):
            gs.visual_system.set_error("Required cards missing")
            return
    
    nc = gs.next_command
    target_player = gs.players.index(nc["target_player"]) + 1
    target_card = gs.players[target_player - 1].characters.index(nc["target_card"]) + 1
    played_card = gs.players[gs.active_player].cards.index(nc["played_card"]) + 1
    
    gs.set_command("p %s %s %s" % (played_card, target_card, target_player))

def discard_button():
    if not gs.selected_card:
        gs.visual_system.set_error("Required card missing")
        return

    if isinstance(gs.selected_card, Character):
        gs.visual_system.set_error("Please don't try to discard characters. Thank you.")
        return

    if gs.selected_card.player != gs.players[gs.active_player]:
        gs.visual_system.set_error("Please only discard your own cards. Thank you.")
        return

    nc = gs.next_command
    nc["played_card"] = gs.selected_card
    played_card = gs.players[gs.active_player].cards.index(nc["played_card"]) + 1
    gs.set_command("d %s" % played_card)

def end_button():
    gs.set_command("e")

def quit_button():
    gs.set_command("q")

def parse_input(info):
    '''
    These commands can technically be sent in textual form.
    This is a remainder of the old version of the game.
    Currently, they are always generated, not written
    (so, the long forms are redundant).
    This may change in the future.
    '''
    print info
    players = gs.players
    if info[0] == 'q' or info[0] == 'quit':
        log("Exiting...")
        print "Bye bye!"
        gs.game_over = True
    elif info[0] == 'd' or info[0] == 'discard':
        assert len(info) == 2, "Wrong number of parameters, 1 expected, got this: %s" % info
        log("Discard", info[1:])
        # 1-based, not 0-based
        assert 0 <= int(info[1])-1 < len(players[gs.active_player].cards), "Invalid card index"
        card = players[gs.active_player].cards[int(info[1])-1]
        players[gs.active_player].discard_card(card = card)
    elif info[0][0] == 'p' or info[0] == 'place' or info[0] == 'play':
        assert len(info) == 4, "Wrong number of parameters, 3 expected, got this: %s" % info
        log("Play card", info[1:])
        card_id = int(info[1]) - 1
        target_card_id = int(info[2]) - 1
        target_player = int(info[3]) - 1
        assert 0 <= card_id < len(players[gs.active_player].cards), "Invalid card index"
        assert 0 <= target_card_id < len(players[target_player].characters), "Invalid target card index"
        card = players[gs.active_player].cards[card_id]
        target_card = players[target_player].characters[target_card_id]

        players[gs.active_player].place_card(card, target_card, int(target_player))
    
    elif info[0] == 'e' or info[0] == 'end':
        assert len(info) == 1, "Wrong number of parameters, 0 expected, got this: %s" % info
        log("End turn")
        gs.moves_made = gs.num_moves_per_turn
    elif info[0] == 'h' or info[0] == 'help':
        gs.print_help = True
    else:
        raise Exception("Unknown move")

def make_move(move_string):
    try:
        assert(0 <= gs.active_player < gs.num_players)
        print_data()

        print move_string
        # This will raise an exception if something goes wrong.
        # And call its appropriate game logic otherwise.
        parse_input(move_string.split(" "))

    except AssertionError as e: # pragma no cover
        clear_screen()
        gs.visual_system.set_error(e)
        print ""
        print "Invalid move!"
        print e
        print ""
    else:
        # Nothing has gone wrong. Yay.
        gs.moves_made += 1
        gs.selected_card = None
        clear_screen()

    if gs.game_over:
        pygame.event.post(pygame.event.Event(pygame.QUIT))

    if gs.moves_made >= gs.num_moves_per_turn:
        gs.players[gs.active_player].draw_until_limit()
        gs.active_player = (gs.active_player + 1) % gs.num_players
        log("Changing active player to %s" % gs.players[gs.active_player].name)
        gs.moves_made = 0

class VisualSystem(Shell):
    def set_error(self, text = ""):
        self.error_msg = str(text)

    def init_pygame(self, game_settings):
        self.game_settings = game_settings
        self.gs = self.game_settings

        pygame.init()
        pygame.display.set_caption("Gloom")

        self.screen_size = gs.screen_size
        self.video_mode_flags = gs.video_mode_flags

        self.screen = pygame.display.set_mode(self.screen_size, self.video_mode_flags)
        self.clock = pygame.time.Clock()

        #self.framerate = 20
        #self.set_timer(1000 // self.framerate)
    
    def init_game(self, game_settings):
        gs = game_settings
        gs.moves_made = 0
        gs.print_help = False  # If set to true, a help message will be displayed before the next move.

        players = create_dummy_players()
        gs.players = players
        gs.active_player = 0

        # TODO: place this somewhere else
        # It's only debug functionality
        chars, modifiers, deaths = create_dummy_cards()
        for id, char in enumerate(chars):
            players[id / gs.characters_per_player].add_character(char)
        for mod in modifiers:
            gs.deck.add_card(mod)
        for death in deaths:
            gs.deck.add_card(death)
        gs.deck.shuffle()

        for player in players:
            player.draw_until_limit()
        clear_screen()

    def begin_frame(self):
        def fit_horizontally(cards, max_width):
            for card in cards:
                self.create_card(card)
            width, height = cards[0].card_surface.get_size()
            if len(cards) * width <= max_width:
                return
            new_width = max_width/len(cards)
            new_height = height * new_width / width
            new_size = (new_width, new_height)
            for card in cards:
                card.card_surface = pygame.transform.scale(card.card_surface, new_size)

        if gs.next_command_ready:
            make_move(gs.next_command["value"])
            gs.next_command_ready = False
            gs.next_command = {}
            self.invalidate()

        self.menu_screen.remove(self.menu)

        for player in gs.players:
            fit_horizontally(player.characters, gs.cards_panel_max_width)
        player_data = Column([
            Column([
                # one entry for each player - name and cards
                Label(player.name),
                Row([ImageButton(card.card_surface, action = card.on_click ) for card in player.characters], align = 't')],
                align = 'l')
            for player in gs.players
        ])

        fit_horizontally(gs.players[gs.active_player].cards, gs.cards_panel_max_width)
        current_player_cards = Row([ImageButton(card.card_surface, action = card.on_click ) for card in gs.players[gs.active_player].cards], align = 't')
        
        current_player_data = Column([Label(gs.players[gs.active_player].name),
        current_player_cards], align = 'l')
        
        cards_panel = Column([player_data, current_player_data], spacing = 30)
        controls = [
            Button("Play card", action = play_button),
            Button("Discard card", action = discard_button),
            Button("End turn", action = end_button),
            Button("Quit", action = quit_button)
        ]
        if gs.selected_card:
            self.create_card(gs.selected_card)
            fit_horizontally([gs.selected_card], gs.controls_panel_max_width)
            controls.append(Image(gs.selected_card.card_surface))
        controls_panel = Column(controls)
        
        self.error_label = Label(self.error_msg)
        self.set_error("")

        self.menu = Column([self.error_label, Row([cards_panel, controls_panel])])
        self.menu_screen.add_centered(self.menu)

    def __init__(self, game_settings):
        self.init_pygame(game_settings)
        self.init_game(game_settings)
        Shell.__init__(self, self.screen)
        
        self.error_msg = ""
        
        self.menu_screen = Screen(self)
        
        self.fontMgr = cFontManager({"default":("VeraBd.ttf", 12, "Resources/fonts"), "large":("VeraBd.ttf", 18, "Resources/fonts"), "arial_large":('arial', 24)})

        self.menu = Row([], align = 't')
        self.menu_screen.add_centered(self.menu)
        self.show_screen(self.menu_screen)
        gs.net.host = ask("Do you want to host a game, or join one?", responses = ["Host", "Join"])
        self.begin_frame()
        if gs.net.host == "Host":
            gs.net.server_ip = get_ip_address("eth0")
            gs.net.server = net.ServerHandler(gs.net.server_ip,gs.net.port)

        # only clients will be asked for an IP
        while not gs.net.server_ip:
            gs.net.server_ip = input_text("Please enter a server's IP:", 150, "82.199.202.5")

        gs.net.client = net.ClientHandler(gs.net.server_ip,gs.net.port)
        id = gs.net.client.send_message("get_player_id")
        print "You are player %s" % id
        gs.player = int(id)
        

        # draw the first frame
        self.begin_frame()

    def create_card(self, card_obj):
        sx = 200
        sy = 278
        margin = 15
        title_height = 20
        mod_size = 30

        card_surface = pygame.image.load("Resources/images/gloom_template_small.png")
        #card_surface = pygame.surface.Surface((sx, sy))
        #card_surface.fill(pygame.color.Color("blue"))
        name_rect = pygame.Rect(margin, 5, sx - 2*margin, title_height)
        #pygame.draw.rect(card_surface, pygame.color.Color("red"), title_rect)
        self.fontMgr.Draw('default', card_obj.get_name(), card_surface, name_rect)
        title_rect = pygame.Rect(margin, 23, sx - 2*margin, title_height)
        self.fontMgr.Draw('default', "Description", card_surface, title_rect, pygame.color.Color("black"))
        description_rect = pygame.Rect(margin, 230, sx - 2*margin, title_height)
        self.fontMgr.Draw('default', "Nobody liked", card_surface, description_rect, pygame.color.Color("white"))
        description_rect = pygame.Rect(margin, 250, sx - 2*margin, title_height)
        self.fontMgr.Draw('default', "this guy.", card_surface, description_rect, pygame.color.Color("white"))
        
        mod_rect = pygame.Rect((0, 0, mod_size, mod_size))
        mod_rect.topleft = (margin, 3 * margin + title_height)

        for mod in card_obj.modifiers:
            if mod:
                color = "red"
                text_color = "white"
                if mod == 0:
                    color = "black"
                elif mod > 0:
                    color = "gray"
                pygame.draw.rect(card_surface, pygame.color.Color(color), mod_rect)
                self.fontMgr.Draw('default', str(mod), card_surface, mod_rect)
            tx, ty = mod_rect.topleft
            ty += mod_size + margin
            mod_rect.topleft = tx, ty
        
        portrait_rect = pygame.Rect(75, 100, 50, 50)
        #pygame.draw.rect(card_surface, pygame.color.Color("red"), portrait_rect)
        portrait_text = "portrait"
        if isinstance(card_obj, Character) and card_obj.is_dead():
            portrait_text = "DEAD"
        self.fontMgr.Draw('large', portrait_text, card_surface, portrait_rect, pygame.color.Color("black"))
        
        card_obj.card_surface = card_surface
        return card_surface
